package hikari.managers;

import com.gooddies.wiring.Wiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.MainWindow;
import hikari.gui.panels.HierarchyTree;
import hikari.gui.panels.Main3dPanel;
import hikari.gui.panels.ModifyPanel;
import hikari.instruments.HikariInstrument;
import hikari.instruments.MoveInstrument;
import hikari.objects3d.Basic3dObject;
import hikari.objects3d.helper_objects.ExtendedEditorCamera;
import hikari.persistence.file.FileSection;
import hikari.persistence.file.HikariFile;
import hikari.preferences.GameProperties;
import hikari.preferences.SceneProperties;
import hikari.script.ProjectCreator;
import hikari.utils.Utils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Timer;
import org.swig.jirr.E_DRIVER_TYPE;
import org.swig.jirr.ExposedVideoData;
import org.swig.jirr.IBillboardSceneNode;
import org.swig.jirr.IGUIEnvironment;
import org.swig.jirr.IGUIStaticText;
import org.swig.jirr.ILightSceneNode;
import org.swig.jirr.ISceneManager;
import org.swig.jirr.ISceneNode;
import org.swig.jirr.ITexture;
import org.swig.jirr.IVideoDriver;
import org.swig.jirr.IrrlichtDevice;
import org.swig.jirr.Jirr;
import org.swig.jirr.MATERIAL_FLAG;
import org.swig.jirr.SColor;
import org.swig.jirr.SColorf;
import org.swig.jirr.SIrrlichtCreationParameters;
import org.swig.jirr.SMaterial;
import org.swig.jirr.camera.BasicCamera;
import org.swig.jirr.recti;
import org.swig.jirr.vector3df;

/**
 * @author sad
 */
@WiringComponent(singleton = true)
public class Manager3D {

    @Wire
    private ModifyPanel modifyPanel;
    private IrrlichtDevice device;
    private ILightSceneNode defaultLight;
    private ExtendedEditorCamera currentEditorCamera;
    private HikariInstrument instrument;
    @Wire
    private HierarchyTree hierarchyTree;
    private Main3dPanel main3dPanel;
    private IGUIStaticText fps;
    private List<BasicCamera> cameras = new ArrayList<>();
    private boolean disableInstruments = false;
    private boolean disableStartEndScene = false;
    private ExposedVideoData exposedData;
    @Wire
    private SceneProperties sceneProperties;
    @Wire
    private GameProperties gameProperties;
    private List<IBillboardSceneNode> alwaysVisibleBilboards = new ArrayList<>();
    private Timer renderingTimer;
    @Wire
    private EngineProperties engineProperties;
    @Wire
    private SelectionManager selectionManager;
    @Wire
    private MainWindow mainWindow;
    @Wire
    private Utils utils;

    public boolean isDisableStartEndScene() {
        return disableStartEndScene;
    }

    public void setDisableStartEndScene(boolean disableStartEndScene) {
        this.disableStartEndScene = disableStartEndScene;
    }

    public boolean isDisabledInstruments() {
        return disableInstruments;
    }

    public void setDisableInstruments(boolean disableInstruments) {
        this.disableInstruments = disableInstruments;
    }

    public void setRenderToTexture(ITexture texture) {
        getVideoDriver().setRenderTarget(texture, true, true, SColor.BLUE);
    }

    public void disableRenderTexture() {
        getVideoDriver().setRenderTarget((ITexture) null, true, true, SColor.GRAY);
    }

    public void addCamera(BasicCamera camera) {
        cameras.add(camera);
    }

    public void removeCamera(BasicCamera camera) {
        cameras.remove(camera);
    }

    public void addAlwaysVisibleBillboard(IBillboardSceneNode billboard) {
        alwaysVisibleBilboards.add(billboard);
    }

    public void removeAlwaysVisibleBillboard(IBillboardSceneNode billboard) {
        alwaysVisibleBilboards.remove(billboard);
    }

    public Main3dPanel getMain3dPanel() {
        return main3dPanel;
    }

    public void setMain3dPanel(Main3dPanel main3dPanel) {
        this.main3dPanel = main3dPanel;
    }

    public void AddObject(Basic3dObject object) {
        hierarchyTree.AddObject(object);
    }

    public IrrlichtDevice initIrrlichtDevice(long hwnd) {
        SIrrlichtCreationParameters params = new SIrrlichtCreationParameters();
        params.setAntiAlias((short) 0);
        params.setBits((short) 32);
        params.setDriverType(E_DRIVER_TYPE.EDT_DIRECT3D9);
        params.setFullscreen(false);
        params.setIgnoreInput(true);
        params.setStencilbuffer(true);
        params.setVsync(false);
        params.setWindowId(hwnd);
        device = Jirr.createDeviceEx(params);
        device.setResizable();
        fps = device.getGUIEnvironment().addStaticText("", new recti(0, 0, 200, 50));
        createDefaultLight();
        createMainCamera();
        initSelectionHandler();
        setInstrument(Wiring.getComponent(MoveInstrument.class));
        initEnd();
        updateSceneProperties();
        return device;
    }

    private void updateSceneProperties() {
        sceneProperties.getAmbientLight();//just invoke it to set internal ambient light variable
    }

    private void initSelectionHandler() {
        selectionManager.addSelectionEvent(new SelectionManager.SelectionEvent() {
            @Override
            public void onSelected(Basic3dObject currentSelection, Basic3dObject newSelection) {
                if (instrument != null) {
                    instrument.selectObject(currentSelection, newSelection);
                }
            }

            @Override
            public boolean isCanSelect(Basic3dObject currentSelection, Basic3dObject newSelection) {
                return true;
            }
        });
    }

    public SceneProperties getSceneProperties() {
        return sceneProperties;
    }

    public GameProperties getGameProperties() {
        return gameProperties;
    }

    public void newProject(String path) throws IOException {
        GameProperties tGameProperties = new GameProperties();
        this.gameProperties = tGameProperties;
        ProjectCreator projectCreator = Wiring.getComponent(ProjectCreator.class);
        projectCreator.createProject(utils.getFilePath(path));
        saveProjectFile(path, tGameProperties);
    }

    public void saveProjectFile(String path, GameProperties properties) throws IOException {
        HikariFile projectFile = new HikariFile(new File(path));
        projectFile.openForWriting();
        FileSection section = projectFile.createNewSection("deviceProperties");
        gameProperties.getDeviceParameters().save(section);
        section.close();
        projectFile.close();
    }

    /**
     * Save project file with default path
     */
    public void saveProjectFile() throws IOException {
        saveProjectFile(gameProperties.getProjectPath(), gameProperties);
    }

    public ExtendedEditorCamera getCamera() {
        return currentEditorCamera;
    }

    public void activateStandardCamera() {
        getSceneManager().setActiveCamera(currentEditorCamera.getRealCamera());
    }

    private void createDefaultLight() {
        defaultLight = getSceneManager().addLightSceneNode((ISceneNode) null, new vector3df(1000, 1000, 0), new SColorf(SColor.WHITE), 9999999f);
    }

    public void removeDefaultLight() {
        if (defaultLight != null) {
            defaultLight.remove();
            defaultLight = null;
        }
    }

    private void createMainCamera() {
        currentEditorCamera = new ExtendedEditorCamera(getSceneManager().getRootSceneNode(), getSceneManager());
        currentEditorCamera.setPosition(new vector3df(0, 0, -100));

    }

    private void initEnd() {
        mainWindow.postStart();
    }

    public HikariInstrument getInstrument() {
        return instrument;
    }

    public void setInstrument(HikariInstrument instrument) {
        if (this.instrument != null) {
            this.instrument.instrumentDeactivate();
        }
        this.instrument = instrument;
        getMain3dPanel().setInstrument(instrument);
        if (this.instrument != null) {
            this.instrument.instrumentActivate();
        }
    }

    public void addRenderingTimer() {
        renderingTimer = new Timer(1, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                render();
            }
        });

        renderingTimer.setRepeats(true);
        renderingTimer.start();
    }

    public void stopRenderingTimer() {
        renderingTimer.setDelay(500);
    }

    public void startRenderingTimer() {
        renderingTimer.setDelay(1);
    }

    public void removeSelectedNode() {
        Basic3dObject object = selectionManager.getCurrentSelectedObject();
        if (object != null) {
            if (!object.onRemove()) {
                return;
            }
            hierarchyTree.removeObject(object);
            modifyPanel.clearModifyPanel();
            selectionManager.clearSelectedObject();
            ISceneNode nodeToDelete = object.getMeshSceneNode();
            nodeToDelete.getParent().removeChild(nodeToDelete);
        }
    }

    public IVideoDriver getVideoDriver() {
        return device.getVideoDriver();
    }

    public ISceneManager getSceneManager() {
        return device.getSceneManager();
    }

    public IGUIEnvironment getGUIEnvironment() {
        return device.getGUIEnvironment();
    }

    private void drawGrid(IVideoDriver driver) {
        if (!engineProperties.getBooleanProperty("viewportDrawGrid")) {
            return;
        }

        driver.resetTransformMatrix();
        SMaterial mat = new SMaterial().setEmissiveColor(engineProperties.getSColorProperty("viewportGridColor"));
        mat.setFlag(MATERIAL_FLAG.EMF_ZBUFFER, true);
        mat.setFlag(MATERIAL_FLAG.EMF_ANTI_ALIASING, true);
        mat.setFlag(MATERIAL_FLAG.EMF_ZWRITE_ENABLE, true);
        driver.setMaterial(mat);
        SColor gridColor = SColor.WHITE;
        vector3df start = new vector3df(0, 0, -50);
        vector3df end = new vector3df(0, 0, 50);
        for (int x = 0; x < 100; x++) {
            driver.draw3DLine(start, end, gridColor);
            start.setX(x - 50);
            end.setX(x - 50);
        }

        start = new vector3df(-50, 0, 0);
        end = new vector3df(50, 0, 0);
        for (int z = 0; z < 100; z++) {
            driver.draw3DLine(start, end, gridColor);
            start.setZ(z - 50);
            end.setZ(z - 50);
        }
    }

    private void updateCursor() {
        if (instrument != null) {
            if (instrument.getCursor() != main3dPanel.getCursor()) {
                main3dPanel.setCursor(instrument.getCursor());
            }
        }
    }

    private void updateCameras() {
        currentEditorCamera.updateCameraTarget();
        for (BasicCamera camera : cameras) {
            camera.updateCameraTarget();
        }
    }

    public void setDrawingWindowHandle(long handle) {
        exposedData = getVideoDriver().createExposedVideoData();
        exposedData.setDirect9Hwnd(handle);
    }

    public void resetWindowHandle() {
        exposedData = null;
    }

    private void drawAlwaysVisibleBillboards() {
        for (IBillboardSceneNode billboard : alwaysVisibleBilboards) {
            billboard.render();
        }
    }

    public void render() {
        updateCameras();
        fps.setText("" + device.getVideoDriver().getFPS());
        if (!disableStartEndScene) {
            device.run();
            if (exposedData != null) {
                getVideoDriver().beginScene(true, true, SColor.GRAY, exposedData);
            } else {
                getVideoDriver().beginScene(true, true, SColor.GRAY);
            }
        }

        instrument.onBeforeRender();
        if (!disableInstruments) {
            modifyPanel.onBeforeRender();
        }

        getSceneManager().drawAll();
        drawGrid(getVideoDriver());
        instrument.onAfterRenderBeforeGui();

        getGUIEnvironment().drawAll();
        drawAlwaysVisibleBillboards();
        if (!disableInstruments) {
            modifyPanel.onAfterRenderBeforeGui();
        }
        if (!disableStartEndScene) {
            getVideoDriver().endScene();
        }

        if (!disableInstruments) {
            modifyPanel.onAfterRender();
            instrument.onAfterRender();
            updateCursor();
        }
    }

    public void reset() {
        sceneProperties = Wiring.getComponent(SceneProperties.class);
        hierarchyTree.removeAllChilds();
        cameras.clear();
        alwaysVisibleBilboards.clear();
        selectionManager.clearSelectedObject();
        getSceneManager().getRootSceneNode().removeAll();
        createMainCamera();
        createDefaultLight();
    }
}